package com.ice.delay.task.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ice.delay.task.core.config.DelayTaskConfig;
import com.ice.delay.task.core.constant.ServerErrorCode;
import com.ice.delay.task.core.enums.ExecuteStatusEnum;
import com.ice.delay.task.core.enums.NotifyStatusEnum;
import com.ice.delay.task.core.helper.DelayTaskDetailRunnableHelper;
import com.ice.delay.task.core.model.DelayTaskDetail;
import com.ice.delay.task.core.model.DelayTaskModule;
import com.ice.delay.task.dao.DelayTaskDetailDao;
import com.ice.delay.task.dao.DelayTaskModuleDao;
import com.ice.delay.task.service.DelayTaskDetailService;
import com.ice.delay.task.vo.req.DelayTaskDetailRetryVoReq;
import com.ice.delay.task.vo.req.DelayTaskDetailVoReq;
import com.ice.delay.task.vo.res.DelayTaskDetailVoRes;
import com.ice.framework.common.base.RPage;
import com.ice.framework.common.exception.BusinessException;
import com.ice.framework.common.util.ObjectTransformUtil;
import com.ice.framework.mybatisplus.util.IDUtil;
import com.ice.framework.mybatisplus.util.MyBatisUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * @author wangwei
 * @Date 2022/4/26 14:25
 */
@Service
public class DelayTaskDetailServiceImpl extends ServiceImpl<DelayTaskDetailDao, DelayTaskDetail> implements DelayTaskDetailService {

    @Resource
    private DelayTaskModuleDao delayTaskModuleDao;

    @Resource
    private TaskExecutor taskExecutor;

    /**
     * 保存延迟任务，
     * 1. 立即执行
     * 2. 未超过1分钟的任务，放入时间轮中执行
     *
     * @param delayTaskDetail
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveDelayTask(DelayTaskDetail delayTaskDetail) {
        DelayTaskModule delayTaskModuleQuery = new DelayTaskModule();
        delayTaskModuleQuery.setModuleCode(delayTaskDetail.getModuleCode());
        DelayTaskModule delayTaskModuleDto = delayTaskModuleDao.find(delayTaskModuleQuery);
        if (ObjectUtil.isEmpty(delayTaskModuleDto)) {
            throw new BusinessException(ServerErrorCode.ERR_DELAY_MODULE_NOT_FOUND);
        }
        // 设置下次执行时间
        Date nextTime = DateUtils.addSeconds(new Date(), delayTaskDetail.getDelayTime());
        delayTaskDetail.setNotifyNextTime(nextTime);
        delayTaskDetail.setExecuteStatus(ExecuteStatusEnum.INIT.getCode());
        // 保存延迟任务(重复的task由唯一索引控制重复)
        String code = IDUtil.generateSTRID("TASK");
        delayTaskDetail.setCode(code);
        delayTaskDetail.setNotifyStatus(0);
        delayTaskDetail.setCreateTime(new Date());
        baseMapper.insert(delayTaskDetail);
        // 判断任务是否立即执行
        if (delayTaskDetail.getImmediatelyExecute()) {
            taskExecutor.execute(() -> DelayTaskDetailRunnableHelper.execute(delayTaskDetail));
        } else {
            // 当延迟执行时间超过1分钟
            if (delayTaskDetail.getDelayTime() > 1000 * 60) {
                return;
            }
            DelayTaskConfig.getDelayTaskConfig().getTimerWheel().addTimerOneTime(delayTaskDetail.getDelayTime(), cb -> {
                // 执行任务
                DelayTaskDetailRunnableHelper.execute(delayTaskDetail);
            });
            // 更新执行状态
            DelayTaskDetail detail = new DelayTaskDetail();
            detail.setCode(code);
            detail.setExecuteStatus(ExecuteStatusEnum.RUNNING.getCode());
            this.updateTaskExecuteStatus(detail);
        }
    }

    @Override
    public Integer updateDelayTask(DelayTaskDetail delayTaskDetail) {
        return this.baseMapper.update(delayTaskDetail, new LambdaUpdateWrapper<DelayTaskDetail>()
                .eq(DelayTaskDetail::getCode, delayTaskDetail.getCode()));
    }

    @Override
    public DelayTaskDetail findTaskDetail(String code) {
        return this.baseMapper.selectOne(new LambdaQueryWrapper<DelayTaskDetail>().eq(DelayTaskDetail::getCode, code));
    }


    /**
     * 更新执行状态
     *
     * @param delayTaskDetail
     */
    public void updateTaskExecuteStatus(DelayTaskDetail delayTaskDetail) {
        this.baseMapper.update(delayTaskDetail, new LambdaUpdateWrapper<DelayTaskDetail>()
                .eq(DelayTaskDetail::getCode, delayTaskDetail.getCode()));
    }

    /**
     * 批量更新执行状态
     */
    @Override
    public int batchUpdateExecuteStatus(List<String> code, Integer status) {
        return this.baseMapper.updateExecuteStatus(code, status);
    }

    @Override
    public RPage<DelayTaskDetailVoRes> pageDetail(RPage<DelayTaskDetailVoReq> page) {
        IPage<DelayTaskDetailVoReq> iPage = new Page<>(page.getCurrent(), page.getSize());
        IPage<DelayTaskDetailVoRes> tenantEntityRPage = this.getBaseMapper().pageDetail(iPage, page.getParameter());
        return MyBatisUtils.transmit(tenantEntityRPage, DelayTaskDetailVoRes.class);
    }

    @Override
    public DelayTaskDetailVoRes findDetail(DelayTaskDetailVoReq req) {
        DelayTaskDetail taskDetail = this.getBaseMapper().selectById(req.getId());
        if (ObjectUtil.isEmpty(taskDetail)) {
            throw new BusinessException(ServerErrorCode.TASK_NOT_FOUND);
        }
        DelayTaskDetailVoRes res = ObjectTransformUtil.transfer(taskDetail, DelayTaskDetailVoRes.class);
        DelayTaskModule delayTaskModuleQuery = new DelayTaskModule();
        delayTaskModuleQuery.setModuleCode(res.getModuleCode());
        DelayTaskModule delayTaskModuleDto = delayTaskModuleDao.find(delayTaskModuleQuery);
        res.setModuleName(delayTaskModuleDto.getModuleName());
        return res;
    }

    /**
     * 重试
     * @Author wangwei
     * @Date 2023/11/15
     */
    @Override
    public void retry(DelayTaskDetailRetryVoReq req) {
        DelayTaskDetail delayTaskDetail = this.baseMapper.selectById(req.getId());
        if (ObjectUtil.isEmpty(delayTaskDetail)) {
            throw new BusinessException(ServerErrorCode.TASK_NOT_FOUND);
        }
        if (!NotifyStatusEnum.FAIL.getCode().equals(delayTaskDetail.getNotifyStatus())) {
            throw new BusinessException(ServerErrorCode.TASK_NOTIFY_STATUS_NOT_CORRECT);
        }
        // 立刻执行任务
        taskExecutor.execute(() -> DelayTaskDetailRunnableHelper.execute(delayTaskDetail));
    }

    /**
     * 查询1分钟 + 5S内的未执行或重试的任务
     *
     * @param nextTime
     * @return
     */
    @Override
    public List<DelayTaskDetail> scheduleTaskQuery(long nextTime) {
        return this.baseMapper.scheduleTaskQuery(nextTime);
    }

    /**
     * 查询执行中的任务
     * @return
     */
    @Override
    public List<DelayTaskDetail> runingTaskQuery() {
        return this.baseMapper.runingTaskQuery();
    }
}
